#!/usr/bin/env ruby

require 'socket'
require 'net/http'
require 'uri'

require 'simmon/logging'
require 'simmon/sensor'

module Simmon

  # Sensor to test whether an HTTP URI is accessible.
  class HttpSensor < Sensor
    # uri: URI instance
    def initialize(name, uri, interval = 60, maxlag = interval * 2, initialState = State.new(true))
      super(name, interval, maxlag, initialState)

      @uri = uri
    end

    # Yield to proc with an active Net::HTTP instance as the first
    # parameter.
    def with_http(&proc)
      Net::HTTP.start(@uri) do |http|
        proc.call(http)
      end
    end
  end

  class HttpGetSensor < HttpSensor
    def sample()
      State.new(Net::HTTP.get_response(@uri).kind_of?(Net::HTTPOK))
    end
  end

  # A minimalistic exporter of sensors over the HTTP protocol. It is
  # intended to be as safe as possible to expose to a non-firewalled
  # port for the purpose of third party monitoring.
  #
  # It does not even attempt to achieve HTTP compliance, and the
  # parsing of the request essentially boils down to a simple
  # substring search. The intent is that the one publish sensors on a
  # given path, where the path is essentially a shared secret.
  class HttpExporter
    def initialize(port, path)
      @port = port
      @magicre = Regexp.new(".*#{path}.*")
      @sensors = []

      @log = Log4r::Logger["simmon"]
    end

    def add_sensor(sensor)
      @sensors << sensor
    end

    def run()
      server = TCPServer.new('0.0.0.0', @port)
      @log.info("http exporter listening on port #{@port}")
      while (sock = server.accept())
        Thread.new() do
          begin
            serve_client(sock)
          rescue
            @log.warn("exception handling client: #{$!}")
            @log.info("exception details: #{$!.backtrace()}")
          end
        end
      end
    end

    def serve_client(sock)
      @log.debug("servling cilent")

      found_secret = false
      end_of_header = false
      while (not end_of_header) and (not sock.eof())
        line = sock.readline().gsub(/\r|\n/, "")
	@log.debug("incline: #{line}")
        if line == '' then
          end_of_header = true
        elsif line =~ @magicre
          found_secret = true
        end
      end

      if found_secret then
        all_ok = true
        errors = ""
        @sensors.each() do |sens|
          state = sens.state()
          if not state.ok() then
            all_ok = false
            errors = "#{errors}\n#{state.message()}"
          end
        end

	def out(sock, msg)
          @log.debug("outline: #{msg}")
          sock.puts(msg)
        end

	out(sock, "HTTP/1.0 200 OK\r")
	out(sock, "Content-Type: text/html\r")
        out(sock, "\r")
        if all_ok then
          out(sock, "<html><body>everything-is-okay</body></html>")
        else
          out(sock, "ALERT:")
          out(sock, errors)
        end         
      else
        @log.warn("client requiest did NOT contain magic")
      end

      sock.close()

      @log.debug("client processing fininshed normally")
    end
  end
end
